﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using collision_and_rigid;
using game_config;
using static game_stuff.PassiveTrait;

namespace game_stuff
{
    public class SaleCertain : ISaleUnit
    {
        public SaleCertain(IMapInteractable? inWhichMapInteractive, GameItem cost, ISaleStuff[] good,
            int stack, GameItem[] orCosts, int configId, IEnumerable<passive_id> passiveIdsMask)
        {
            InWhichMapInteractive = inWhichMapInteractive;
            Cost = cost;
            Good = good;
            Stack = stack;
            OrCosts = orCosts;
            ConfigId = configId;
            PassiveIdsMask = passiveIdsMask;
            DoneDictionary = new Dictionary<int, int>();
        }

        public SaleCertain(IMapInteractable? inWhichMapInteractive, GameItem cost, ISaleStuff[] good,
            int stack, GameItem[] orCosts, Dictionary<int, int> doneDictionary, int configId,
            IEnumerable<passive_id> passiveIdsMask)
        {
            InWhichMapInteractive = inWhichMapInteractive;
            Cost = cost;
            Good = good;
            Stack = stack;
            OrCosts = orCosts;
            ConfigId = configId;
            PassiveIdsMask = passiveIdsMask;
            DoneDictionary = doneDictionary;
        }

        public IMapInteractable PutInteractable(TwoDPoint pos, bool isActive)
        {
            return new ApplyDevice(this, pos, isActive);
        }

        public IMapInteractable? InWhichMapInteractive { get; set; }

        public GameItem Cost { get; set; }
        public GameItem[] OrCosts { get; }

        public ISaleStuff[] GetGood()
        {
            return Good;
        }

        public ISaleStuff? GetTitleGood()
        {
            var firstOrDefault = Good.FirstOrDefault(x =>
            {
                return x switch
                {
                    PassiveTrait passiveTrait => passiveTrait.CanTitle(),
                    _ => true
                };
            });
            return firstOrDefault;
        }

        public int GetRestStack(int? gid)
        {
            return SaleUnitStandard.GetRestStack(gid, this);
        }


        public int ConfigId { get; }

        public IEnumerable<passive_id> PassiveIdsMask { get; }
        public ISaleStuff[] Good { get; private set; }

        public Dictionary<int, int> DoneDictionary { get; }

        public int Stack { get; }

        public bool CanInterActOneBy(CharacterStatus characterStatus)
        {
            return SaleUnitStandard.CanInterActOneBy(characterStatus, this);
        }

        public bool CanInterActTwoBy(CharacterStatus characterStatus)
        {
            return SaleUnitStandard.CanInterActTwoBy(characterStatus, this);
        }


        public ImmutableArray<IActResult> ActWhichChar(CharacterStatus characterStatus, MapInteract interactive)
        {
            return SaleUnitStandard.ActWhichChar(characterStatus, interactive, this);
        }

        public static ISaleUnit GenByConfig(sale_unit saleUnit, IEnumerable<passive_id> passiveIdsMask)
        {
            var firstOrDefault = saleUnit.Cost.FirstOrDefault();

            var genByConfigGain = firstOrDefault == null
                ? new GameItem(item_id.coin, 0)
                : GameItem.GenByConfigGain(firstOrDefault);

            var passiveIds = passiveIdsMask as passive_id[] ?? passiveIdsMask.ToArray();
            ISaleStuff[] saleStuff = saleUnit.SaleType switch
            {
                sale_type.prop => new ISaleStuff[] { Prop.GenById(saleUnit.LimitIdRange.ChooseRandOne()) },
                sale_type.passive => GenPassiveSaleStuff(saleUnit, passiveIds),
                sale_type.weapon => new ISaleStuff[] { Weapon.GenById(saleUnit.LimitIdRange.ChooseRandOne()) },
                sale_type.reroll => new ISaleStuff[] { new ReRoll() },
                sale_type.item => new ISaleStuff[]
                {
                    GameItem.GenByConfig(saleUnit.LimitIdRange.ChooseRandOne(), 1) ??
                    throw new Exception($"not such item {saleUnit.LimitIdRange.ChooseRandOne()}")
                },

                _ => throw new ArgumentOutOfRangeException()
            };

            var gameItems = new GameItem[] { };
            var unit = new SaleCertain(null, genByConfigGain, saleStuff, saleUnit.Stack,
                gameItems, saleUnit.id, passiveIds);
            return unit;
        }

        private static ISaleStuff[] GenPassiveSaleStuff(sale_unit saleUnit, IEnumerable<passive_id> passiveIdsMask)
        {
            return GenManyByPId(
                    (saleUnit.LimitIdRange.Any()
                        ? saleUnit.LimitIdRange.Select(x =>
                            x.TryStringToEnum<passive_id>(out var id)
                                ? id
                                : throw new Exception($"not such passiveId {x}"))
                        : GetPassiveFromShopConfig(saleUnit.RareMax, saleUnit.PassiveShopTypeRange))
                    .Except(passiveIdsMask).ToList()
                    .ChooseRandOne()
                    , 1).OfType<ISaleStuff>()
                .ToArray();
        }

        private static IEnumerable<passive_id> GetPassiveFromShopConfig(int saleUnitRareMax,
            int[] saleUnitPassiveShopTypeRange)
        {
            return CommonConfig.Configs.shop_passive_unlocks.Values.Where(x => x.PassiveRare <= saleUnitRareMax).Where(
                xx =>
                    !saleUnitPassiveShopTypeRange.Any() || saleUnitPassiveShopTypeRange.Contains(xx.PassiveShopType)
            ).Select(xxx => xxx.passive_id);
        }

        public (ContainType type, int intId) ReRollGood()
        {
            if (!CommonConfig.Configs.sale_units.TryGetValue(ConfigId, out var saleUnit))
                throw new Exception($"{ConfigId} is Not Found");

            var chooseRandCanSame = saleUnit.LimitIdRange.ChooseRandOne();
            var saleStuff = saleUnit.SaleType switch
            {
                sale_type.prop => new ISaleStuff[] { Prop.GenById(chooseRandCanSame) },
                sale_type.passive => GenPassiveSaleStuff(saleUnit, PassiveIdsMask),
                sale_type.weapon => new ISaleStuff[] { Weapon.GenById(chooseRandCanSame) },
                sale_type.reroll => Good,
                sale_type.item => new ISaleStuff[]
                {
                    GameItem.GenByConfig(chooseRandCanSame, 1) ??
                    throw new Exception($"not such item {chooseRandCanSame}")
                },
                _ => throw new ArgumentOutOfRangeException()
            };
            Good = saleStuff;
            if (saleUnit.SaleType==sale_type.reroll)
            {
                return (ContainType.ReRollC, -1);
            }
            var getSaleId = (Good.FirstOrDefault(x =>
                {
                    if (!(x is PassiveTrait passiveTrait)) return true;
                    var id = (passive_id)passiveTrait.GetId();
                    if (!CommonConfig.Configs.passives.TryGetValue(id, out var passive))
                        throw new KeyNotFoundException($"not such passive id {id}");
                    return !passive.CantTitle;
                }
            ) ?? throw new Exception($"not any item in {Good}")).GetSaleId();
            return getSaleId;
        }
    }
}